#include "pccom.h"

// * 如果外设使用到其他外设(尤其是使用了引脚, 需要借用GPIO)
// * 则需要将其他外设也进行配置
// 外设一般配置顺序:
// 1. 如果有RCC, 先配置RCC
// 2. 如果有xxx_Init, 先准备对应的InitTypedef的结构体存放初始化参数
// 3. 调用XXX_Init使用初始化结构体的值进行配置
// >> 如果有中断相关配置, 则进行中断配置
//      >> 先开启外设内部的中断源(XXX_ITConfig())
//      >> 再开启通道本身(NVIC_Init())
//      >> 编写对应的中断处理函数
// 4. 如果有XXX_CMD, 表示带有开关功能, 则打开开关

void PCCOM_init(void)
{
    // 1. GPIO设置
    // 1.2 GPIO时钟使能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 1.3 发送端Tx配置
    GPIO_InitTypeDef gpio_initStruct = {0};
    // 推挽式复用功能（Alternate Function Push-Pull）
    // 适用场景：USART TX、SPI、PWM输出
    gpio_initStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_initStruct.GPIO_Pin = PCCOM_Pin_Tx;
    gpio_initStruct.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(PCCOM_Port, &gpio_initStruct);

    // 1.4 接收端Rx配置
    // 输入上拉（Input Pull-Up）
    // 内部连接上拉电阻，默认状态为高电平（1），外部需要拉低才能改变状态
    gpio_initStruct.GPIO_Mode = GPIO_Mode_IPU;
    gpio_initStruct.GPIO_Pin = PCCOM_Pin_Rx;
    GPIO_Init(PCCOM_Port, &gpio_initStruct);

    // 2. 配置UART
    // USART时钟使能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    // 最常用的配置为：8bit数据位，1bit起始位，1bit停止位，无校验
    // 简写为：8/1/1，none
    USART_InitTypeDef USART_initStruct = {0};
    // 波特率：通常为4800、9600、115200
    USART_initStruct.USART_BaudRate = PCCOM_baudRate;
    // 硬件流控：大部分不在使用硬件流控，将其失能
    USART_initStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    // 发送和接收的单独功能是否开启
    USART_initStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    // 校验，有无校验No、奇校验Odd和偶校验Even，但现在基本不使用
    USART_initStruct.USART_Parity = USART_Parity_No;
    // 停止位的长度
    USART_initStruct.USART_StopBits = USART_StopBits_1;
    // 数据位的长度
    USART_initStruct.USART_WordLength = USART_WordLength_8b;
    // 初始化USART
    USART_Init(USART1, &USART_initStruct);

    // TC标志位默认为1，容易导致第一个发送的字符被忽略，因此初始化时手动清零
    // USART_ClearFlag(USART1, USART_FLAG_TC);
    USART_GetFlagStatus(USART1, USART_FLAG_TC);

    // 3. 配置中断IT
    // 先配置中断源
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 是否接收到数据
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 是否接收完数据
    // 开启中断通道
    NVIC_InitTypeDef NVIC_initStruct = {0};
    // 对应的中断通道，在stm32f10x.h中枚举定义
    NVIC_initStruct.NVIC_IRQChannel = USART1_IRQn;
    // 使能或者禁用的选项
    NVIC_initStruct.NVIC_IRQChannelCmd = ENABLE;
    // 抢占优先级，抢占优先级 + 响应优先级 的种类共有16（0000~1111）种
    NVIC_initStruct.NVIC_IRQChannelPreemptionPriority = 2;
    // 响应优先级
    NVIC_initStruct.NVIC_IRQChannelSubPriority = 2;
    // 初始化中断
    NVIC_Init(&NVIC_initStruct);

    // 使能USART
    USART_Cmd(USART1, ENABLE);
}

// 重写fputc，以实现printf()可以向连接电脑的USART1写数据
/**
 * @brief 重写的fputc必须与库的头文件声明一致
 *        用途是将其字节不发送到文件，而是直接输入到USART1
 * @param c 待发送的字符
 * @param stream 原本需要发送到的文件流，此处不再使用
 * @return int 发送完毕的字符
 */
int fputc(int c, FILE * stream)
{
    PCCOM_sendData(c);
    while(SET != USART_GetFlagStatus(USART1, USART_FLAG_TC))
    {}
    return c;
}
